#include "scps.h"
#include "scps_ip.h"
#include "scpstp.h"
#include "scpsudp.h"
#include "ll.h"
#include "tp_debug.h"
#include "rs_config.h"
#include "other_proto_handler.h"

extern void *memset (void *s, int c, size_t n);
extern struct msghdr out_msg;
extern struct llbuf *in_data;


extern GW_ifs gw_ifs;

extern tp_Socket *tp_allsocs;   /* Pointer to first TP socket */

extern int scps_udp_port;
void cause_reset (unsigned char *pkt);

void icmp_Handler (scps_np_rqts *rqts_in, int ip_pkt_len, ip_template *hdr, int offset)
{

  struct icmphdr *i_hdr;
  int keep_it = -1;
  i_hdr = (struct icmphdr *) ((void *) in_data->data + offset);
  switch (i_hdr -> type) {
  case ICMP_ECHOREPLY:
  case ICMP_SOURCE_QUENCH:
  case ICMP_REDIRECT:
  case ICMP_ECHO:
  case ICMP_ROUTERADVERT:
  case ICMP_ROUTERSOLICIT:
  case ICMP_TIME_EXCEEDED:
  case ICMP_PARAMETERPROB:
  case ICMP_TIMESTAMP:
  case ICMP_TIMESTAMPREPLY:
  case ICMP_INFO_REQUEST:
  case ICMP_INFO_REPLY:
  case ICMP_ADDRESS:
  case ICMP_ADDRESSREPLY:
    keep_it = 0;
    break;
  case ICMP_DEST_UNREACH:
    keep_it = icmp_unreachable (i_hdr);
    break;
  default:
    LOG ("ICMP unknown option\n");
    break;
  }
  if (keep_it == 0) {
    other_proto_Handler (in_data->gwio , &in_data, MAX_MTU, &offset, rqts_in, IPPROTO_ICMP);
  }

  if (keep_it == 1) {
    cause_reset ((void *) in_data->data + offset + 8);
  }

  if (keep_it == -1) {
    LOG ("unknown code\n");
  }
  return;
}

void cause_reset (unsigned char *pkt)
{
  tp_Socket *s;
  ip_template *ip;
  tp_Header *tp;  
  scps_np_rqts arqts;
  scps_np_rqts *rqts = &arqts;


  ip = (in_Header *) ((void *) pkt);

  if (inv4_GetProtocol (ip) == IPPROTO_TCP) {
     tp = (tp_Header *) (((void*) ip + 20));
  } else  {
     return;
  }


  for (s = tp_allsocs; s; s = s->next) {
    if (ntohs (tp->dstPort) == ntohs (s->hisport) &&
      ntohs (tp->srcPort) == ntohs (s->myport) &&
      ntohl (ip->nl_head.ipv4.destination) == ntohl (s->his_ipv4_addr) &&
      ntohl (ip->nl_head.ipv4.source) == ntohl (s->my_ipv4_addr)) {
      break;
    }
  }
  if (s) {
    rqts->tpid = inv4_GetProtocol (ip);
    rqts->ipv4_src_addr = ntohl (ip->nl_head.ipv4.destination);
    rqts->ipv4_dst_addr = ntohl (ip->nl_head.ipv4.source);
    rqts->cksum = 0;
    rqts->nl_protocol = NL_PROTOCOL_IPV4;
    rqts->DSCP = (unsigned char ) ((ntohs (ip->nl_head.ipv4.vht)) & 0x00ff);
    tp_Abort (s->sockid);

    if (s->peer_socket) {
      tp_Abort (s->peer_socket->sockid);
    }
  }
}

int scps_icmp_output ()
{
  return (0);
}


int icmp_unreachable (struct icmphdr *i_hdr)
{
  ip_template *ip;
  int rc = 0;
  udp_Header *uh;

  ip = ( (ip_template *) ((void *) (i_hdr) + 8));

  if (inv4_GetProtocol (ip) == IPPROTO_TCP) {
    rc = 1;
  }
  if (inv4_GetProtocol (ip) == IPPROTO_UDP) {
    uh = (udp_Header *) ((void*) ip + 20);
    if ((ntohs (uh->srcPort) == scps_udp_port)  || (ntohs (uh->dstPort) == scps_udp_port)) {
      rc = 1;
      LOG ("ERROR: Peer gateway IP (%x) may be down\n", (unsigned int) (ntohl (ip->nl_head.ipv4.source)));
    }
  }
  return (rc);
}


